package cz.incad.psh.pshassign.server.search;

import cz.incad.psh.pshassign.server.Options;
import cz.incad.psh.pshassign.server.StopWords;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URLEncoder;
import java.util.ArrayList;
import org.apache.commons.io.IOUtils;
import org.json.JSONArray;
import org.json.JSONObject;

/**
 *
 * @author alberto
 */
public class PSHSearch {

    private String q;
    private double csPrefLabel;
    private double enPrefLabel;
    private double csAltLabel;
    private double enAltLabel;
    private double titleBoost;
    private double annotaceBoost;
    private double subjectBoost;
    private double konferenceBoost;
    ArrayList<String> stopWords;
    String action;
    String strength;

    public String doSearch(JSONObject jsSolr) {
        try {

            strength = Options.getOption("assign", "strength");
            csPrefLabel = Options.getDouble("assign", "boost_csPrefLabel");
            enPrefLabel = Options.getDouble("assign", "boost_enPrefLabel");
            csAltLabel = Options.getDouble("assign", "boost_csAltLabel");
            enAltLabel = Options.getDouble("assign", "boost_enAltLabel");
            titleBoost = Options.getDouble("assign", "boost_title");
            annotaceBoost = Options.getDouble("assign", "boost_annotace");
            subjectBoost = Options.getDouble("assign", "boost_subjects");
            konferenceBoost = Options.getDouble("assign", "boost_konference");

            
            
            JSONObject jsonResponse = jsSolr.getJSONObject("response");
            int numDocs = jsonResponse.getInt("numFound");
            if (numDocs == 0) {
                return "Id not found";
            } else {

                String q = "";
                String boostQuery = "";


                JSONObject doc = jsonResponse.getJSONArray("docs").getJSONObject(0);
                String title = doc.getString("title");
                title = cleanString(title);


                String annotace = doc.optString("annotace");

                JSONArray konferencesArray = doc.optJSONArray("konference");
                if (konferencesArray != null) {
                    String konferences = "";
                    for (int i = 0; i < konferencesArray.length(); i++) {
                        String val = konferencesArray.optString(i);
                        if (val != null) {
                            konferences += " " + val;
                        }
                    }
                    boostQuery += " (" + cleanString(konferences.toLowerCase()) + ")^" + konferenceBoost;
                }

                JSONArray termVectors = jsSolr.getJSONArray("termVectors").getJSONArray(3);

                int annotace_index = -1;
                int subjects_index = -1;
                int title_index = -1;
                int all_index = -1;
                for (int i = 0; i < termVectors.length(); i++) {
                    if ("annotace_vectors".equals(termVectors.optString(i))) {
                        annotace_index = i + 1;
                    } else if ("subject_vectors_str".equals(termVectors.optString(i))) {
                        subjects_index = i + 1;
                    } else if ("title_vectors".equals(termVectors.optString(i))) {
                        title_index = i + 1;
                    } else if ("all_vectors".equals(termVectors.optString(i))) {
                        all_index = i + 1;
                    }
                }

                int subs = 0;
                if (subjects_index > 0) {
                    JSONArray subject_vectors_str = termVectors.getJSONArray(subjects_index);
                    subs = subject_vectors_str.length();
                    
                }  
                    if (all_index > 0) {
                        JSONArray all_vectors = termVectors.getJSONArray(all_index);
                        for (int i = 0; i < all_vectors.length(); i++) {
                            String val = all_vectors.optString(i++);
                            if (val != null) {
                                q += " " + val;
                            }
                        }
                    }
                
                JSONArray subjectsA = doc.optJSONArray("subject");
                String subjects = "";
                if (subjectsA != null) {
                    for (int i = 0; i < subjectsA.length(); i++) {
                        String val = subjectsA.optString(i);
                        if (val != null) {
                            val = cleanString(val).trim();
                            if(val.length()>0){
                                subjects += " " + val;
                            }
                        }
                    }
                    
                    if(subjects.length()>0){
                        boostQuery += " (" + subjects + ")^" + subjectBoost;
                    }
                }
                if (annotace.length() > 0 && subs < 6) {
                    boostQuery += " (" + cleanString(annotace.toLowerCase()) + ")^" + annotaceBoost;
                }


                String qf = "_text _text_en csPrefLabel^" + (csPrefLabel * 8)
                        + " enPrefLabel^" + enPrefLabel
                        + " csAltLabel^" + csAltLabel
                        + " enAltLabel^" + enAltLabel;

                String hl = "&hl=true&hl.fl=csPrefLabel_hl,enPrefLabel_hl,csAltLabel_hl,enAltLabel_hl"
                        + "&hl.simple.pre=<b>&hl.simple.post=</b>&hl.boundaryScanner=breakIterator"
                        + "&hl.mergeContiguous=true&hl.snippets=5&hl.fragsize=0";

                String fl = "score,*";

                if (title.length() > 0) {
                    boostQuery += " (" + title + ")^" + titleBoost;
                    //boostQuery += " (\"" + title + "\")^" + titleBoost;
                    //boostQuery += " csPrefLabel:(" + title + ")^" + titleBoost;

                }

                q = cleanQuery(q.toLowerCase());
                boostQuery = cleanQuery(boostQuery);
//                String urlStr = "http://localhost:8983/solr/PSH/select?wt=json&fl=" + fl + "&rows=200"
//                        + "&defType=edismax&q.op=OR"
//                        + hl
//                        + "&qf=" + URLEncoder.encode(qf, "UTF-8")
//                        + "&bq=" + URLEncoder.encode(boostQuery, "UTF-8")
//                        + "&q=" + URLEncoder.encode(q, "UTF-8");

//                java.net.URL url = new java.net.URL(urlStr);
////                System.out.println(urlStr);
//                InputStream is;
//                is = url.openStream();
//                StringWriter writer = new StringWriter();
//                IOUtils.copy(is, writer, "UTF-8");
//                return writer.toString();
                
                String urlParameters = "wt=json&fl=" + fl + "&rows=" + Options.getInt("assign", "max_solr_keys")
                        + "&defType=edismax&q.op=OR"
                        + hl
                        + "&qf=" + URLEncoder.encode(qf, "UTF-8")
                        + "&bq=" + URLEncoder.encode(boostQuery, "UTF-8")
                        + "&q=" + URLEncoder.encode(q, "UTF-8");
                
//                System.out.println("urlParameters -> " + urlParameters);
                StringWriter res = new StringWriter();
                postData(new StringReader(urlParameters), res);
                return res.toString();

            }


        } catch (Exception e) {
            System.out.println("error :\n" + e);
            e.printStackTrace();
            return "error " + e;
        }
    }
    
    private void postData(Reader data, StringWriter output)
            throws Exception {
        String request = Options.getOption("psh", "solr") + "/select";
        java.net.URL url = new java.net.URL(request); 
        HttpURLConnection urlc = null;
        String POST_ENCODING = "UTF-8";
        try {
            urlc = (HttpURLConnection) url.openConnection();
            try {
                urlc.setRequestMethod("POST");
            } catch (ProtocolException e) {
                throw new Exception("Shouldn't happen: HttpURLConnection doesn't support POST??", e);
            }
            urlc.setDoOutput(true);
            urlc.setDoInput(true);
            urlc.setUseCaches(false);
            urlc.setAllowUserInteraction(false);
            urlc.setRequestProperty("Content-type", "application/x-www-form-urlencoded; charset=" + POST_ENCODING);

            OutputStream out = urlc.getOutputStream();

            try {
                Writer writer = new OutputStreamWriter(out, POST_ENCODING);
                pipe(data, writer);
                writer.close();
            } catch (IOException e) {
                throw new Exception("IOException while posting data", e);
            } finally {
                if (out != null) {
                    out.close();
                }
            }

            InputStream in = urlc.getInputStream();
            int status = urlc.getResponseCode();
            StringBuilder errorStream = new StringBuilder();
            try {
                if (status != HttpURLConnection.HTTP_OK) {
                    errorStream.append("postData URL=").append(request).append(" HTTP response code=").append(status).append(" ");
                    throw new Exception("URL=" + request + " HTTP response code=" + status);
                }
                
                
                
                IOUtils.copy(in, output, "UTF-8");
                
//                Reader reader = new InputStreamReader(in);
//                pipeString(reader, output);
//                reader.close();
            } catch (IOException e) {
                throw new Exception("IOException while reading response", e);
            } finally {
                if (in != null) {
                    in.close();
                }
            }

            InputStream es = urlc.getErrorStream();
            if (es != null) {
                try {
                    Reader reader = new InputStreamReader(es);
                    pipeString(reader, errorStream);
                    reader.close();
                } catch (IOException e) {
                    throw new Exception("IOException while reading response", e);
                } finally {
                    if (es != null) {
                        es.close();
                    }
                }
            }
            if (errorStream.length() > 0) {
                throw new Exception("postData error: " + errorStream.toString());
            }

        } catch (IOException e) {
            throw new Exception("Solr has throw an error. Check tomcat log. " + e);
        } finally {
            if (urlc != null) {
                urlc.disconnect();
            }
        }
    }
    
    /**
     * Pipes everything from the reader to the writer via a buffer
     */
    private void pipe(Reader reader, Writer writer) throws IOException {
        char[] buf = new char[1024];
        int read = 0;
        while ((read = reader.read(buf)) >= 0) {
            writer.write(buf, 0, read);
        }
        writer.flush();
    }

    /**
     * Pipes everything from the reader to the writer via a buffer except lines
     * starting with '<?'
     */
    private void pipeString(Reader reader, StringBuilder writer) throws IOException {
        char[] buf = new char[102400];
        int read = 0;
        while ((read = reader.read(buf)) >= 0) {
            if (!(buf[0] == '<' && buf[1] == '?')) {
                writer.append(buf, 0, read);
            }
        }
    }

    
    private void post(String urlParameters) throws Exception{
        String request = "http://localhost:8983/solr/PSH/select";
        java.net.URL url = new java.net.URL(request); 
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();           
        connection.setDoOutput(true);
        connection.setDoInput(true);
        connection.setInstanceFollowRedirects(false); 
        connection.setRequestMethod("POST"); 
        connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); 
        connection.setRequestProperty("charset", "utf-8");
        connection.setRequestProperty("Content-Length", "" + Integer.toString(urlParameters.getBytes().length));
        connection.setUseCaches (false);

        DataOutputStream wr = new DataOutputStream(connection.getOutputStream ());
        wr.writeBytes(urlParameters);
        wr.flush();
        wr.close();
        connection.disconnect();
    }
    

    private String cleanString(String old) throws Exception {
        return StopWords.removeStopWords(old.toLowerCase()).trim().replace("\u00A0", " ")
                        .replaceAll("\\)", "").replaceAll("\\(", "")
                        .replaceAll("\\[", "").replaceAll("\\]", "")
                        .replaceAll("\"", "")
                        .replaceAll("[:\\.\\,\\^]", "").trim();
    }
    private String cleanQuery(String old) throws Exception {
        String s = old.toLowerCase();
        s = StopWords.removeStopWords(s);
        s = s
                .replaceAll("\\+", " ")
                .replaceAll("\\!", " ")
                .replaceAll("\\-", " ")
                .replaceAll("\\/", " ")
                .replaceAll("\\s+", " ")
                .replaceAll("\\( \\)", "()")
                .replaceAll("\\?", "")
                .replaceAll("~", "")
                .replaceAll("\\(\\)", "")
                .replaceAll("\\\\", "")
                .replaceAll("\\{", "")
                .replaceAll("\\}", "")
                .replaceAll("\\(\\)\\^\\d\\s", "")
                .replaceAll("\\(\\)\\^\\d.\\d\\s", "");

        s = s.replaceAll("/[^A-Za-z0-9 ]/", "");

//        if (s.length() > 1500) {
//            s = s.substring(0, 1500);
//            s = s.substring(0, s.lastIndexOf(" "));
//        }

        return s;
    }
}
